#pragma once 
#include <bits/stdc++.h>

/*****************************************************************************
 *! @brief C++11 增强了模板功能，在C++11之前, 类与函数模板只支持固定数量的模板参数, C++11
 *!        允许定义包含0到任意数量的模板参数. 声明可变参数模板时需要在typename和class后带
 *!        省略号"...". 省略号的作用有两个;
 *           1). 声明一个参数包, 该参数包可包含0到任意数量模板参数;
 *           2). 在模板定义的右边, 可以将参数包展开成一个个独立的参数;
 * 
 *  
 ****************************************************************************/

//! 1. 统计可变参数的个数
template<typename... Args>
int count_args(Args&&... args) {
    std::cout << sizeof...(args) << std::endl;
    return 0;
}

//! 2. 包展开
/**
 * @brief 包展开有两种方式：
 *   1).  通过递归展开
 *   2).  通过逗号表达式+初始化列表的方式
 */
//! 2.1 递归展开
// void printArgs() {                 //! 参数可以为空的终止方式
//     std::cout << "empty" << std::endl;
// }

template<typename T>                  //! 必须有一个参数的终止方式
void printArgs(T last) {
    std::cout << last << std::endl;
}

template<typename T, typename... Args>
void printArgs(T head, Args... rest) {
    std::cout << head << " ";
    printArgs(rest...);     // 展开
}


//! 2.2 初始化列表方式展开
template<typename... Args>
void expand(Args... args) {
    //! 这里的printArgs不用理解为是一个递归终止函数, 就是一个普通的处理参数包中每个参数的函数
    /**
     *! @brief 逗号表达式 d = ( a = b, c), b 先赋值给 a, 然后逗号表达式返回c, 即d = c; 
     * 
     *!     这里使用...使用初始化列表的方式将结果展开成一个动态的数组, 组中的arr将包含参数个0   
     */
    int arr[] = { (printArgs(args), 0)... };
    // int arr[] = { args... };    //! 如果args中全部是整数的话，也可以使用这样的表达
    // std::initializer_list<int>{ (printArgs(args), 0)...};                         // v2
    // std::initializer_list<int>{ ([=]{ std::cout << args << std::endl; }, 0)...};  // v3
    std::cout << "Num of args: "<<  sizeof(arr) / sizeof(arr[0]) << std::endl;
}

//! 3. 可变参数模板类
/**
 *! @brief     可变参数模板类是一个带可变模板参数的模板类， std::tuple就是一个可变的模板类
 * 
 *! Notice:    可变参数模板类的展开方式与可变参数函数的展开方式不一样， 前者只能通过模板特化的方式展开 
 *            1) 递归+特化方式展开
 *            2) 继承方式展开
 */

//! 3.1 递归方式
template<typename... Args>     // TODO: 这部分的前向声明可以去掉
struct Sum {
    // code nothing here
};

// 部分展开，告诉编译器如何展开
template<typename Head, typename... Rest>
struct Sum<Head, Rest...> {
    enum {
        value = Sum<Head>::value + Sum<Rest...>::value
    };
};

// 特化终止
template<typename Last>
struct Sum<Last> {
   enum {
        value = sizeof(Last)
   };
};

// 使用两个类型终止递归
// template<typename First, Typename Last>
// struct Sum<First, Last> {
//    enum {
//         value = sizeof(First) + sizeof(Last)
//    };
// };

// 使用0个类型终止递归
// template<>
// struct Sum<> {
//    enum {
//         value = 0
//    };
// };


//! 3.2 继承方式
// 基础情况：没有任何模板参数时的类定义
template<typename... Args>
class ExpandArgs {};

// 递归继承的模板类：处理第一个参数，并继承余下的参数
template<typename T, typename... Rest>
class ExpandArgs<T, Rest...> : public ExpandArgs<Rest...> {
public:
    ExpandArgs(T first, Rest... rest) : ExpandArgs<Rest...>(rest...), value(first) {}

    void process() {
        // 处理当前参数
        std::cout << value << std::endl;
        // 处理剩余参数
        ExpandArgs<Rest...>::process();
    }

private:
    T value;
};

// 特化：处理最后一个参数
template<typename T>
class ExpandArgs<T> {
public:
    ExpandArgs(T value) : value(value) {}

    void process() {
        std::cout << value << std::endl;
    }

private:
    T value;
};

// 使用可变模板参数实现对象创建工厂
// template<typename T, typename... Args>           //! 上述的Args是按值拷贝的,我们可以实现更优化的版本
// T* Instance(Args... args) {
//     return new T(args...);
// }

template<typename T, typename... Args>
T* Instance(Args&&... args) {
    return new T(std::forward<Args>(args)...);      //! 使用完美转发实现更优化的版本, 注意...的位置
}


int variable_args();